﻿using Esponce.QRCode.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Esponce.QRCode
{
	public class QRCodeStatistics
	{
		#region Private members
		private QRCodeClient client;
		#endregion

		#region Constructors
		/// <summary>
		/// Creates a new instance of QRCodeStatistics.
		/// </summary>
		/// <param name="apiKey">Esponce API key to authenticate user.</param>
		public QRCodeStatistics(string apiKey)
		{
			this.client = new QRCodeClient() { ApiKey = apiKey };
		}

		/// <summary>
		/// Creates a new instance of QRCodeStatistics.
		/// </summary>
		/// <param name="client">An instance of QRCodeClient for HTTP connections. API key should be set!</param>
		public QRCodeStatistics(QRCodeClient client)
		{
			this.client = client;
		}
		#endregion

		#region Public properties
		/// <summary>
		/// Gets an instance of QRCodeClient that performs HTTP connections.
		/// </summary>
		public QRCodeClient Client
		{
			get
			{
				return this.client;
			}
		}
		#endregion

		#region Public methods
		/// <summary>
		/// Gets individual scan records.
		/// </summary>
		/// <param name="id">QR Code identifier.</param>
		/// <returns></returns>
		public async Task<IEnumerable<Record>> GetRecordsAsync(string id)
		{
			return await client.GetTrackStatisticsAsync(id);
		}

		/// <summary>
		/// Gets scans by days.
		/// </summary>
		/// <param name="id">QR Code identifier.</param>
		/// <returns>Returns scans by day.</returns>
		public async Task<List<DateRecord>> GetScansByDayAsync(string id)
		{
			var list = (await GetRecordsAsync(id)).ToList();
			return (await GetScansByDayAsync(list)).ToList();
		}

		/// <summary>
		/// Gets scans by days.
		/// </summary>
		/// <param name="records">List of individual scans.</param>
		/// <returns>Returns scans by day.</returns>
		public async Task<IEnumerable<DateRecord>> GetScansByDayAsync(IEnumerable<Record> records)
		{
			var result =
			 (from x in records
				where x.Date != null //All records should have "Date" set but this fuse is just in case...
				group x by x.Date.Value into g
				select new DateRecord
				{
					Date = g.Key,
					Count = g.Count()
				});

			return result.OrderByDescending(x => x.Date).AsEnumerable();
		}

		/// <summary>
		/// Gets scans by device manufacturer and device model name.
		/// </summary>
		/// <param name="id">QR Code identifier.</param>
		/// <remarks>
		/// x.Name = "N/A" for unknown device
		/// </remarks>
		/// <returns>Returns scans by device manufacturer and model name.</returns>
		public async Task<List<DeviceRecord>> GetScansByDeviceAsync(string id)
		{
			var list = (await GetRecordsAsync(id)).ToList();
			return (await GetScansByDeviceAsync(list)).ToList();
		}

		/// <summary>
		/// Gets scans by device manufacturer and device model name.
		/// </summary>
		/// <param name="records">List of individual scans.</param>
		/// <returns>Returns scans by device manufacturer and model name.</returns>
		/// <remarks>
		/// x.Name = "N/A" for unknown device
		/// </remarks>
		public async Task<IEnumerable<DeviceRecord>> GetScansByDeviceAsync(IEnumerable<Record> records)
		{
			var results =
			 (from x in records.ToList()
				where !string.IsNullOrEmpty(x.DeviceName)
				group x by x.DeviceName into g
				let rec = records.First(x => x.DeviceName == g.Key)
				select new DeviceRecord()
				{
					Name = g.Key,
					Count = g.Count(),
					Platform = rec.SystemName,
					ModelName = rec.DeviceModel,
					Manufacturer = rec.DeviceManufacturer,
				});

			//Return records of known device names
			var list = results.OrderByDescending(x => x.Count).ToList();

			//Return records of unknown device names
			int unknown = records.Count(x => string.IsNullOrEmpty(x.DeviceName));
			list.Add(new DeviceRecord()
			{
				Count = unknown
			});

			return list;
		}

		/// <summary>
		/// Gets scans by platform (OS name), e.g. Android, iOS, Windows Phone
		/// </summary>
		/// <param name="id">QR Code identifier.</param>
		/// <remarks>
		/// x.Platform = "N/A" for unknown platforms
		/// </remarks>
		/// <returns>Returns scans by platform.</returns>
		public async Task<List<DeviceRecord>> GetScansByPlatformAsync(string id)
		{
			var list = (await GetRecordsAsync(id)).ToList();
			return (await GetScansByPlatformAsync(list)).ToList();
		}

		/// <summary>
		/// Gets scans by platform (OS name), e.g. Android, iOS, Windows Phone
		/// </summary>
		/// <param name="records">List of individual scans.</param>
		/// <remarks>
		/// x.Platform = "N/A" for unknown platforms
		/// </remarks>
		/// <returns>Returns scans by platform.</returns>
		public async Task<IEnumerable<DeviceRecord>> GetScansByPlatformAsync(IEnumerable<Record> records)
		{
			var results =
			 (from x in records
				where !string.IsNullOrEmpty(x.SystemName)
				group x by x.SystemName into g
				select new DeviceRecord
				{
					Platform = g.Key,
					Count = g.Count()
				});

			//Return records of known platforms
			var list = results.OrderByDescending(x => x.Count).ToList();

			//Return records of unknown platforms
			int unknown = records.Count(x => string.IsNullOrEmpty(x.SystemName));
			list.Add(new DeviceRecord()
			{
				Platform = null, //Not available
				Count = unknown
			});

			return list;
		}

		/// <summary>
		/// Gets scans by country.
		/// </summary>
		/// <param name="id">QR Code identifier.</param>
		/// <remarks>
		/// x.Name = "--" for unknown country
		/// </remarks>
		/// <returns>Returns scans by platform.</returns>
		public async Task<List<CountryRecord>> GetScansByCountryAsync(string id)
		{
			var list = (await GetRecordsAsync(id)).ToList();
			return (await GetScansByCountryAsync(list)).ToList();
		}

		/// <summary>
		/// Gets scans by country.
		/// </summary>
		/// <param name="records">List of individual scans.</param>
		/// <remarks>
		/// x.Name = "--" for unknown country
		/// </remarks>
		/// <returns>Returns scans by platform.</returns>
		public async Task<IEnumerable<CountryRecord>> GetScansByCountryAsync(IEnumerable<Record> records)
		{
			var results =
			 (from x in records
				where !string.IsNullOrEmpty(x.CountryCode)
				group x by x.CountryCode into g
				select new CountryRecord
				{
					CountryCode = g.Key,
					Count = g.Count()
				}).ToList();

			//Return records of known platforms
			var list = results.OrderByDescending(x => x.Count).ToList();

			//Return records of unknown platforms
			int unknown = records.Count(x => string.IsNullOrEmpty(x.CountryCode));
			list.Add(new CountryRecord()
			{
				CountryCode = null, //Not available
				Count = unknown
			});

			return list;
		}
		#endregion
	}
}
